Session 3 Cheat Sheet — Data Visualisation with ggplot2
Applied Methods · MA Public Administration
1. Choose the plot
Before writing code, ask:
- What do I want to show?
- How many variables do I have?
- Are they categorical or continuous?
- Do I want to show a distribution, comparison, relationship, or trend?
| Situation | Typical plot | Useful function |
|---|---|---|
| One categorical variable | Bar plot | geom_bar() |
| One continuous variable | Histogram | geom_histogram() |
| One continuous variable | Density plot | geom_density() |
| One continuous variable | Boxplot | geom_boxplot() |
| One continuous variable | Cumulative distribution | stat_ecdf() |
| Continuous × continuous | Scatterplot | geom_point() |
| Categorical × continuous | Boxplot | geom_boxplot() |
| Categorical × continuous | Violin plot | geom_violin() |
| Categorical × continuous | Raw observations | geom_jitter() |
| Categorical × categorical | Grouped bar plot | geom_bar(position = "dodge") |
| Categorical × categorical | Proportional bar plot | geom_bar(position = "fill") |
| Categorical × categorical | Tile plot | geom_tile() |
| Time × continuous | Line plot | geom_line() |
2. The structure of a ggplot
The general structure is:
ggplot(
data = data,
aes(
x = x_variable,
y = y_variable
)
) +
geom_...()A more complete plot often follows this structure:
ggplot(
data = data,
aes(
x = x_variable,
y = y_variable
)
) +
geom_...() +
scale_...() +
facet_...() +
labs(...) +
theme_...()The main components are:
| Component | Purpose |
|---|---|
ggplot() |
Define the data and general mappings |
aes() |
Map variables to visual properties |
geom_*() |
Choose how observations are represented |
scale_*() |
Control how variables are displayed |
facet_*() |
Split the plot into panels |
labs() |
Change titles, axes, legends, captions |
theme_*() |
Change the general appearance |
theme() |
Modify specific non-data elements |
3. Useful ggplot2 commands at a glance
Geometric objects
| Task | Function |
|---|---|
| Bar plot | geom_bar() |
| Bar plot from precomputed values | geom_col() |
| Histogram | geom_histogram() |
| Density plot | geom_density() |
| Boxplot | geom_boxplot() |
| Violin plot | geom_violin() |
| Scatterplot | geom_point() |
| Jittered points | geom_jitter() |
| Line plot | geom_line() |
| Smoothed relationship | geom_smooth() |
| Tile plot | geom_tile() |
| Text | geom_text() |
| Label | geom_label() |
| Horizontal reference line | geom_hline() |
| Vertical reference line | geom_vline() |
| Cumulative distribution | stat_ecdf() |
Aesthetic mappings
| Visual property | Argument |
|---|---|
| Horizontal position | x |
| Vertical position | y |
| Colour | color |
| Fill colour | fill |
| Shape | shape |
| Size | size |
| Transparency | alpha |
| Group | group |
| Line type | linetype |
Example:
ggplot(
data,
aes(
x = x,
y = y,
color = group
)
) +
geom_point()4. Mapping versus setting aesthetics
Inside aes(), a graphical property depends on a variable.
ggplot(
data,
aes(
x = x,
fill = group
)
) +
geom_bar()Outside aes(), the property is fixed.
ggplot(
data,
aes(x = x)
) +
geom_bar(
fill = "grey70",
color = "black"
)Rule of thumb:
aes(color = variable)→ colour represents information in the datacolor = "black"→ every observation receives the same colour
5. Command map
Start a plot
ggplot()
ggplot(data)
ggplot(data, aes())Map variables
aes(
x = x,
y = y,
color = group,
fill = group,
shape = group,
size = variable,
alpha = variable,
group = group
)One categorical variable
geom_bar()
geom_col()One continuous variable
geom_histogram()
geom_density()
geom_boxplot()
stat_ecdf()Two continuous variables
geom_point()
geom_smooth()Categorical and continuous variables
geom_boxplot()
geom_violin()
geom_jitter()Two categorical variables
geom_bar(position = "dodge")
geom_bar(position = "fill")
geom_tile()Time series
geom_line()Change bar position
position = "stack"
position = "fill"
position = "dodge"Control transparency
alpha = 0.5Change point size
size = 2Histogram bins
bins = 30
binwidth = 5Change labels
labs()
xlab()
ylab()
ggtitle()Change scales
scale_x_continuous()
scale_y_continuous()
scale_x_log10()
scale_y_log10()
scale_color_viridis_c()
scale_color_viridis_d()
scale_fill_viridis_c()
scale_fill_viridis_d()Useful label functions
scales::label_percent()
scales::label_number()
scales::label_comma()Facets
facet_wrap()
facet_grid()Coordinates
coord_flip()
coord_cartesian()
coord_fixed()Themes
theme_minimal()
theme_classic()
theme_bw()
theme()Annotations
geom_text()
geom_label()
annotate()
geom_hline()
geom_vline()Combine plots
p1 + p2
p1 / p2
(p1 + p2) / p3
plot_annotation()These commands require the patchwork package.
Save plots
ggsave()6. Reusable templates
Bar plot: counts
ggplot(
data,
aes(x = category)
) +
geom_bar() +
theme_minimal() +
labs(
x = "Category",
y = "Number of observations"
)Bar plot: proportions
ggplot(
data,
aes(
x = category,
y = after_stat(prop),
group = 1
)
) +
geom_bar() +
scale_y_continuous(
labels = scales::label_percent()
) +
theme_minimal() +
labs(
x = "Category",
y = "Share"
)Order categories
data %>%
count(category) %>%
ggplot(
aes(
x = reorder(category, n),
y = n
)
) +
geom_col() +
coord_flip() +
theme_minimal()Histogram
ggplot(
data,
aes(x = x)
) +
geom_histogram(
bins = 30
) +
theme_minimal() +
labs(
x = "X",
y = "Number of observations"
)Histogram with fixed bin width
ggplot(
data,
aes(x = x)
) +
geom_histogram(
binwidth = 5
) +
theme_minimal()Density plot
ggplot(
data,
aes(x = x)
) +
geom_density() +
theme_minimal()Boxplot
ggplot(
data,
aes(y = x)
) +
geom_boxplot() +
theme_minimal()Scatterplot
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point() +
theme_minimal()Scatterplot with transparency
Useful when many observations overlap.
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point(
alpha = 0.5
) +
theme_minimal()Scatterplot with trend
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point() +
geom_smooth(
method = "lm",
se = FALSE
) +
theme_minimal()Continuous variable across categories
ggplot(
data,
aes(
x = category,
y = y
)
) +
geom_boxplot() +
theme_minimal()Violin plot
ggplot(
data,
aes(
x = category,
y = y
)
) +
geom_violin() +
theme_minimal()Raw observations across categories
ggplot(
data,
aes(
x = category,
y = y
)
) +
geom_jitter(
width = 0.15,
alpha = 0.5
) +
theme_minimal()Combine boxplot and observations
ggplot(
data,
aes(
x = category,
y = y
)
) +
geom_boxplot(
outlier.shape = NA
) +
geom_jitter(
width = 0.15,
alpha = 0.5
) +
theme_minimal()Two categorical variables: grouped bars
ggplot(
data,
aes(
x = category1,
fill = category2
)
) +
geom_bar(
position = "dodge"
) +
theme_minimal()Two categorical variables: proportions
ggplot(
data,
aes(
x = category1,
fill = category2
)
) +
geom_bar(
position = "fill"
) +
scale_y_continuous(
labels = scales::label_percent()
) +
theme_minimal()Tile plot
First count the combinations.
data %>%
count(
category1,
category2
) %>%
ggplot(
aes(
x = category1,
y = category2,
fill = n
)
) +
geom_tile() +
theme_minimal()Line plot
ggplot(
data,
aes(
x = time,
y = y
)
) +
geom_line() +
theme_minimal()Several lines
ggplot(
data,
aes(
x = time,
y = y,
group = group
)
) +
geom_line() +
theme_minimal()Several lines with colour
ggplot(
data,
aes(
x = time,
y = y,
color = group
)
) +
geom_line() +
theme_minimal()7. Adding a third variable
With colour
ggplot(
data,
aes(
x = x,
y = y,
color = group
)
) +
geom_point() +
theme_minimal()With shape
ggplot(
data,
aes(
x = x,
y = y,
shape = group
)
) +
geom_point(
size = 2
) +
theme_minimal()With size
ggplot(
data,
aes(
x = x,
y = y,
size = z
)
) +
geom_point(
alpha = 0.6
) +
theme_minimal()With facets
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point() +
facet_wrap(
~ group
) +
theme_minimal()Rule of thumb:
- colour / fill / shape → useful for categorical variables
- size / colour gradient → useful for continuous variables
- facets → useful when separate panels are easier to compare
Avoid mapping too many variables at once.
8. Facets
One faceting variable
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point() +
facet_wrap(
~ group
)Control number of columns
facet_wrap(
~ group,
ncol = 2
)Two faceting variables
facet_grid(
group1 ~ group2
)Free scales
facet_wrap(
~ group,
scales = "free"
)Fixed scales make panels easier to compare directly.
Free scales can make variation within each panel easier to see.
9. Labels and communication
Titles and axis labels
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point() +
labs(
title = "Main title",
subtitle = "Additional information",
x = "X-axis label",
y = "Y-axis label",
caption = "Source: ..."
)Legend title
The argument name corresponds to the mapped aesthetic.
labs(
color = "Group"
)or
labs(
fill = "Group"
)or
labs(
shape = "Group"
)10. Scales
Format large numbers
scale_x_continuous(
labels = scales::label_comma()
)Format percentages
If the variable ranges from 0 to 1:
scale_y_continuous(
labels = scales::label_percent()
)If the variable ranges from 0 to 100:
scale_y_continuous(
labels = scales::label_percent(
scale = 1
)
)Add a suffix
scale_y_continuous(
labels = scales::label_number(
suffix = " CHF"
)
)Logarithmic scale
scale_x_log10()or
scale_y_log10()A logarithmic scale can be useful when a continuous variable is highly skewed.
11. Themes
Built-in themes
theme_minimal()
theme_classic()
theme_bw()Modify individual elements
theme(
legend.position = "bottom",
axis.text.x =
element_text(
angle = 45,
hjust = 1
),
panel.grid.minor =
element_blank(),
plot.title =
element_text(
face = "bold"
)
)Common theme elements:
legend.position
axis.text
axis.text.x
axis.text.y
axis.title
plot.title
plot.subtitle
panel.grid
panel.grid.major
panel.grid.minor12. Coordinates
Flip axes
Useful for long category names.
ggplot(
data,
aes(
x = category,
y = value
)
) +
geom_col() +
coord_flip()Zoom without filtering data
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point() +
coord_cartesian(
xlim = c(0, 100),
ylim = c(0, 100)
)coord_cartesian() changes what is displayed.
It does not remove observations from the data.
13. Annotations and reference lines
Horizontal reference line
geom_hline(
yintercept = 50,
linetype = "dashed"
)Vertical reference line
geom_vline(
xintercept = 50,
linetype = "dashed"
)Add labels to observations
geom_text(
aes(label = name)
)or:
geom_label(
aes(label = name)
)Label only selected observations
ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point() +
geom_label(
data = data %>%
filter(condition),
aes(label = name)
)14. Combining several plots
Load patchwork:
library(patchwork)Create plots:
p1 <- ggplot(
data,
aes(x = x)
) +
geom_histogram()
p2 <- ggplot(
data,
aes(x = y)
) +
geom_histogram()Side by side
p1 + p2Above each other
p1 / p2More complex layout
(p1 + p2) / p3Add a common title
(p1 + p2) / p3 +
plot_annotation(
title = "Main title",
subtitle = "Subtitle"
)15. Save a figure
Save the most recent plot:
ggsave(
filename = "figure.png",
width = 8,
height = 5,
dpi = 300
)Save a plot object:
p <- ggplot(
data,
aes(
x = x,
y = y
)
) +
geom_point()
ggsave(
filename = "figure.png",
plot = p,
width = 8,
height = 5,
dpi = 300
)16. A reusable publication-style template
ggplot(
data,
aes(
x = x,
y = y,
color = group
)
) +
geom_point(
alpha = 0.7,
size = 2
) +
theme_minimal() +
theme(
legend.position = "bottom",
panel.grid.minor =
element_blank(),
plot.title =
element_text(
face = "bold"
)
) +
labs(
title = "Main message of the figure",
subtitle = "Additional information",
x = "X variable",
y = "Y variable",
color = "Group",
caption = "Source: ..."
)17. Final rule of thumb
When creating a visualisation, first identify what you want to show.
If you want to…
- show counts of categories →
geom_bar() - show the distribution of a continuous variable →
geom_histogram()/geom_density() - summarize a distribution →
geom_boxplot() - compare a continuous variable across groups →
geom_boxplot()/geom_violin()/geom_jitter() - show the relationship between two continuous variables →
geom_point() - show change over time →
geom_line() - compare two categorical variables → grouped or proportional
geom_bar() - add another categorical variable →
color,fill,shape, or facets - add another continuous variable →
sizeor a colour gradient - split the same plot across groups →
facet_wrap()/facet_grid() - change labels →
labs() - change how values are displayed →
scale_*() - change the appearance of the plot →
theme_*()/theme() - zoom without removing observations →
coord_cartesian() - combine several plots →
patchwork - save the final figure →
ggsave()
A useful workflow is:
# 1. Identify the question
# 2. Identify the variable types
# 3. Choose the appropriate geom
# 4. Build the simplest possible plot
# 5. Add additional variables only if useful
# 6. Improve labels, scales, and theme
# 7. Check whether the figure is easy to interpret
# 8. Save the final figureThe important skill is not memorizing every geom_*() function.
It is learning to identify which visual representation makes the comparison you care about easiest to see.